package com.hina.rec.application.service.bill.impl;

import cn.hutool.core.codec.Base64;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.hina.rec.application.convert.BillDetailConvert;
import com.hina.rec.application.service.bill.BillDetailService;
import com.hina.rec.domain.mapper.BillDetailMapper;
import com.hina.rec.domain.model.BillDetailDO;
import com.hina.rec.endpoint.req.BillDetailEditReq;
import com.hina.rec.endpoint.req.BillDetailImportReq;
import com.hina.rec.endpoint.req.BillDetailSearchReq;
import com.hina.rec.endpoint.res.BillDetailRes;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.csv.CSVFormat;
import org.apache.commons.csv.CSVParser;
import org.apache.commons.csv.CSVRecord;
import org.apache.poi.ss.usermodel.*;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;

/**
 * 对账单服务实现类
 *
 * @author wangjianhua
 * @since 2025-05-24
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class BillDetailServiceImpl implements BillDetailService {

    private final BillDetailMapper billDetailMapper;

    @Override
    public IPage<BillDetailRes> search(BillDetailSearchReq req) {
        Page<BillDetailDO> page = new Page<>(req.getPageNum(), req.getPageSize());
        LambdaQueryWrapper<BillDetailDO> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(req.getBillVersion() != null, BillDetailDO::getBillVersion, req.getBillVersion())
                .eq(req.getYear() != null, BillDetailDO::getYear, req.getYear())
                .eq(req.getMonth() != null, BillDetailDO::getMonth, req.getMonth())
                .eq(req.getSencMemberId() != null, BillDetailDO::getSencMemberId, req.getSencMemberId())
                .like(req.getSencMemberName() != null, BillDetailDO::getSencMemberName, req.getSencMemberName())
                .eq(req.getExpandCustomers() != null, BillDetailDO::getExpandCustomers, req.getExpandCustomers())
                .like(req.getSubProductName() != null, BillDetailDO::getSubProductName, req.getSubProductName());
        IPage<BillDetailDO> resultPage = billDetailMapper.selectPage(page, wrapper);
        return resultPage.convert(BillDetailConvert.INSTANCE::toRes);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void edit(BillDetailEditReq req) {
        BillDetailDO billDetailDO = BillDetailConvert.INSTANCE.toDO(req);
        if (billDetailDO.getId() == null) {
            billDetailMapper.insert(billDetailDO);
        } else {
            billDetailMapper.updateById(billDetailDO);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean importData(MultipartFile file) {

        List<BillDetailDO> billDetailList = new ArrayList<>();

        try {
            String type = file.getContentType();
            byte[] fileBytes = file.getBytes();
            if (type.endsWith("sheet")) {
                // Excel文件处理
                billDetailList = parseExcel(fileBytes);
            } else if (type.endsWith("csv")) {
                // CSV文件处理
                billDetailList = parseCsv(fileBytes);
            } else {
                throw new RuntimeException("不支持的文件类型");
            }

            // 批量保存数据
            if (!billDetailList.isEmpty()) {
                // 插入数据之前，先删除之前导入的相同年月数据；
                int year = billDetailList.get(0).getYear();
                int month = billDetailList.get(0).getMonth();
                LambdaQueryWrapper<BillDetailDO> wrapper = new LambdaQueryWrapper<>();
                wrapper.eq(BillDetailDO::getYear, year).eq(BillDetailDO::getMonth, month);
                billDetailMapper.delete(wrapper);
                // 批量插入数据
                billDetailMapper.insert(billDetailList);
            }

            log.info("成功导入{}条数据", billDetailList.size());
            return true;
        } catch (Exception e) {
            log.error("导入对账单数据失败", e);
            throw new RuntimeException("导入对账单数据失败：" + e.getMessage());
        }
    }

    /**
     * 解析Excel文件
     */
    private List<BillDetailDO> parseExcel(byte[] fileBytes) throws IOException {
        List<BillDetailDO> billDetailList = new ArrayList<>();
        try (Workbook workbook = WorkbookFactory.create(new ByteArrayInputStream(fileBytes))) {
            Sheet sheet = workbook.getSheetAt(0);
            for (int i = 1; i <= sheet.getLastRowNum(); i++) {
                Row row = sheet.getRow(i);
                if (row == null) {
                    continue;
                }
                billDetailList.add(createBillDetail(index -> getCellValue(row.getCell(index))));
            }
        }
        return billDetailList;
    }

    /**
     * 解析CSV文件
     */
    private List<BillDetailDO> parseCsv(byte[] fileBytes) throws IOException {
        List<BillDetailDO> billDetailList = new ArrayList<>();
        try (CSVParser parser = CSVFormat.DEFAULT.parse(new InputStreamReader(new ByteArrayInputStream(fileBytes), StandardCharsets.UTF_8))) {
            boolean isFirst = true;
            for (CSVRecord record : parser) {
                if (isFirst) {
                    isFirst = false;
                    continue;
                }
                billDetailList.add(createBillDetail(record::get));
            }
        }
        return billDetailList;
    }

    /**
     * 创建账单明细对象
     *
     * @param valueGetter 值获取器
     * @return 账单明细对象
     */
    private BillDetailDO createBillDetail(Function<Integer, String> valueGetter) {
        BillDetailDO billDetail = new BillDetailDO();
        int start = -1;
        billDetail.setBillVersion("current");
        //
        String yearMonth = valueGetter.apply(start + 1);
        String[] ym = yearMonth.split("-");
        billDetail.setYear(getInt(ym[0]));
        billDetail.setMonth(getInt(ym[1]));
        //
        billDetail.setSencMemberName(valueGetter.apply(start + 3));
        billDetail.setSencMemberId(valueGetter.apply(start + 4));
        //
        billDetail.setExpandCustomers(valueGetter.apply(start + 5));
        billDetail.setSubProductName(valueGetter.apply(start + 6));
        billDetail.setSubProductType(valueGetter.apply(start + 7));
        billDetail.setVersion(valueGetter.apply(start + 8));
        billDetail.setPartnerProductCode(valueGetter.apply(start + 9));
        billDetail.setProductFeeType(valueGetter.apply(start + 10));
        //
        start += 1;

        billDetail.setQueryCount(getInt(valueGetter.apply(start + 11)));
        billDetail.setQueryResultCount(getInt(valueGetter.apply(start + 12)));
        billDetail.setReduceNum(getInt(valueGetter.apply(start + 13)));
        billDetail.setSuccCount(getInt(valueGetter.apply(start + 14)));
        //
        start += 2;
        billDetail.setProductPrice(getBigDecimal(valueGetter.apply(start + 15)));
        billDetail.setSuccAmount(getBigDecimal(valueGetter.apply(start + 16)));
        billDetail.setTechFee(getBigDecimal(valueGetter.apply(start + 17)));
        //
        start += 2;
        billDetail.setInAmount(getBigDecimal(valueGetter.apply(start + 18)));
        
        // 数据源1
        start += 1;
        billDetail.setCounterName1(valueGetter.apply(start + 19));
        billDetail.setCounterPrice1(getBigDecimal(valueGetter.apply(start + 20)));
        billDetail.setCounterFeeType1(valueGetter.apply(start + 21));
        billDetail.setCounterCount1(getInt(valueGetter.apply(start + 22)));
        billDetail.setCounterCost1(getBigDecimal(valueGetter.apply(start + 23)));
        
        // 数据源2
        billDetail.setCounterName2(valueGetter.apply(start + 24));
        billDetail.setCounterPrice2(getBigDecimal(valueGetter.apply(start + 25)));
        billDetail.setCounterFeeType2(valueGetter.apply(start + 26));
        billDetail.setCounterCount2(getInt(valueGetter.apply(start + 27)));
        billDetail.setCounterCost2(getBigDecimal(valueGetter.apply(start + 28)));
        
        // 数据源3
        billDetail.setCounterName3(valueGetter.apply(start + 29));
        billDetail.setCounterPrice3(getBigDecimal(valueGetter.apply(start + 30)));
        billDetail.setCounterFeeType3(valueGetter.apply(start + 31));
        billDetail.setCounterCount3(getInt(valueGetter.apply(start + 32)));
        billDetail.setCounterCost3(getBigDecimal(valueGetter.apply(start + 33)));
        
        // 数据源4
        billDetail.setCounterName4(valueGetter.apply(start + 34));
        billDetail.setCounterPrice4(getBigDecimal(valueGetter.apply(start + 35)));
        billDetail.setCounterFeeType4(valueGetter.apply(start + 36));
        billDetail.setCounterCount4(getInt(valueGetter.apply(start + 37)));
        billDetail.setCounterCost4(getBigDecimal(valueGetter.apply(start + 38)));
        
        // 数据源5
        billDetail.setCounterName5(valueGetter.apply(start + 39));
        billDetail.setCounterPrice5(getBigDecimal(valueGetter.apply(start + 40)));
        billDetail.setCounterFeeType5(valueGetter.apply(start + 41));
        billDetail.setCounterCount5(getInt(valueGetter.apply(start + 42)));
        billDetail.setCounterCost5(getBigDecimal(valueGetter.apply(start + 43)));
        
        // 数据源6
        billDetail.setCounterName6(valueGetter.apply(start + 44));
        billDetail.setCounterPrice6(getBigDecimal(valueGetter.apply(start + 45)));
        billDetail.setCounterFeeType6(valueGetter.apply(start + 46));
        billDetail.setCounterCount6(getInt(valueGetter.apply(start + 47)));
        billDetail.setCounterCost6(getBigDecimal(valueGetter.apply(start + 48)));
        
        billDetail.setTotalCost(getBigDecimal(valueGetter.apply(start + 49)));
        //
        start += 1;
        billDetail.setRemark(valueGetter.apply(start + 50));
        billDetail.setEnvironment(valueGetter.apply(start + 51));

        return billDetail;
    }

    private int getInt(String countStr) {
        if (countStr == null || countStr.isBlank()) {
            return 0;
        }
        int end = countStr.indexOf(".");
        end = end > 0 ? end : countStr.length();
        return Integer.valueOf(countStr.substring(0, end));
    }

    private BigDecimal getBigDecimal(String value) {
        if (value == null || value.isBlank()) {
            return BigDecimal.ZERO;
        }
        return new BigDecimal(value);
    }

    /**
     * 获取单元格值
     */
    private String getCellValue(Cell cell) {
        if (cell == null) {
            return "";
        }
        switch (cell.getCellType()) {
            case STRING:
                return cell.getStringCellValue();
            case NUMERIC:
                if (DateUtil.isCellDateFormatted(cell)) {
                    return cell.getDateCellValue().toString();
                }
                return String.valueOf(cell.getNumericCellValue());
            case BOOLEAN:
                return String.valueOf(cell.getBooleanCellValue());
            case FORMULA:
                return cell.getCellFormula();
            default:
                return "";
        }
    }
} 